/*******************************************************************************
  Copyright(c) 2009 Geoffrey Hausheer. All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it 
  under the terms of the GNU General Public License as published by the Free 
  Software Foundation; either version 2 of the License, or (at your option) 
  any later version.
  
  This program is distributed in the hope that it will be useful, but WITHOUT 
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
  more details.
  
  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59 
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
  The full GNU General Public License is included in this distribution in the
  file called LICENSE.

  Contact Information: gcx-at-phracturedblue-dot-com
*******************************************************************************/

#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "winnt.h"
#include <winsock.h>
#include <stdio.h>

#define TRACE wprintf
#define FIXME wprintf

static HANDLE hSerial = 0;
static int sock = -1;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	TRACE(L"(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);

	switch (fdwReason)
	{
		case DLL_PROCESS_ATTACH:
			/* FIXME: Initialisation */
			//DisableThreadLibraryCalls(hinstDLL);
			break;
			break;
		case DLL_PROCESS_DETACH:
			break;
		default:
			break;
	}

	return TRUE;
}

HANDLE SerialConnect()
{
	DCB dcbSerialParams = {0};
	COMMTIMEOUTS timeouts={0};
	HANDLE hSer;

	hSer = CreateFile("COM5",
			GENERIC_READ | GENERIC_WRITE,
			0,
			0,
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			0);
	if(hSer != INVALID_HANDLE_VALUE) {
		dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
		if (GetCommState(hSerial, &dcbSerialParams)) {
			dcbSerialParams.BaudRate=CBR_9600;
			dcbSerialParams.ByteSize=8;
			dcbSerialParams.StopBits=ONESTOPBIT;
			dcbSerialParams.Parity=NOPARITY;
			SetCommState(hSer, &dcbSerialParams);

			timeouts.ReadIntervalTimeout=50;
			timeouts.ReadTotalTimeoutConstant=50;
			timeouts.ReadTotalTimeoutMultiplier=10;
			timeouts.WriteTotalTimeoutConstant=50;
			timeouts.WriteTotalTimeoutMultiplier=10;
			SetCommTimeouts(hSer, &timeouts);
		}
	}
	return hSer;
}


void sersend(HANDLE hSer, char *data)
{
	DWORD dwBytesWritten = 0;
	DWORD len;
	len = strlen(data);
	if(len) {
		WriteFile(hSer, data, len, &dwBytesWritten, NULL);
	}
}

void socksend(int sd, char *data)
{
	DWORD len;
	len = strlen(data);
	if(len) {
		send(sd, data, len, 0);
	}
}

int cx_connect(char *host, int port)
{
	struct hostent *hent;
	struct sockaddr_in sockadr;
	int sock;

	hent = gethostbyname(host);
	if (hent == NULL) {
		wprintf(L"cx_connect: cannot find host %s, error %d\n", host, h_errno);
		return -1;
	}
	sock = socket(PF_INET, SOCK_STREAM, 0);
	if (sock == -1) {
		wprintf(L"cx_connect: cannot create socket\n");
		return -1;
	}
	sockadr.sin_family = AF_INET;
	sockadr.sin_addr.s_addr = *((unsigned *)(hent->h_addr));
	sockadr.sin_port = htons(port);
//	d3_printf("h_length = %d adr = %08x\n", hent->h_length, *(hent->h_addr));
	if (connect(sock, (struct sockaddr *)(&sockadr), sizeof(sockadr))) {
		wprintf(L"cx_connect: could not connect: %d - %s\n", errno, strerror(errno));
		closesocket(sock);
		return -1;
	}
	return sock;
}

/******************************************************************
 *		GETBUFFER (CMOSDLL.8)
 *
 *
 */
static int odd = 0;
void __stdcall GETBUFFER(short *ptr, int len)
{
	int i, j, color;
	FIXME(L"GETBUFFER %08x len: %d\n", ptr, len);
	if (sock > 0) {
		char *cptr = (char *)ptr;
		char data[256], *start;
		while(len) {
			int next_len = len > 65536 ? 65536 : len;
			int len1;
			sprintf(data, "read track %d\n", next_len);
			socksend(sock, data);
			//Fetch header
			len1 = recv(sock, data, 256, 0);
			start = strstr(data, "\n");
			*start++ = 0;
			printf("Received %d bytes (%d real) %s\n", len1, len1 - (start - data), data);
			len1 -= (start - data);
			memcpy(cptr, start, len1);
			cptr += len1;
			next_len -= len1;
			len -= len1;
			while(next_len ) {
				len1 = recv(sock, cptr, next_len, 0);
				printf("Received(2) %d bytes (requested %d)\n", len1, next_len);
				if(len1 <= 0) {
					wprintf(L"Read Failed\n");
					return;
				}
				cptr += len1;
				len -= len1;
				next_len -= len1;
			}
		}
		return;
	}
	for(i = 0; i < 1024; i++) {
		for(j = 0; j < 1280; j++) {
/*
			if(odd) {
				color = 65535  * j / 1280;
			} else {
				color = 65535 * i / 1024;
			}
*/
			if(i == 630 && j == 510) {
				color = 45000;
			} else {
				color = 0;
			}
			*ptr++ = color;
		}
	}
	odd ^= 1;
}

/******************************************************************
 *		GuideCommand (CMOSDLL.9)
 *
 *
 */
__stdcall void GuideCommand(int direction, int hsec)
{
	int msec = hsec * 10;
	char str[80] = {0};
	FIXME(L"GuideCommand Direction: %08x %dmsec\n", direction, msec);
	if(hSerial == 0) {
		hSerial = SerialConnect();
	}
	if(hSerial == INVALID_HANDLE_VALUE) {
		return;
	}
	sersend(hSerial, ":RM#"); //:RG#
	if(direction & 0x80) {
		wprintf(L"Guiding west...\n");
		
		//sersend(hSerial, ":Mw#");
		//Sleep(msec);
		//sersend(hSerial, ":Qw#");
		sprintf(str, ":Mgw%04d#", msec);
		sersend(hSerial, str);
	}
	if(direction & 0x40) {
		wprintf(L"Guiding north...\n");
		sprintf(str, ":Mgn%04d#", msec);
		sersend(hSerial, str);
	}
	if(direction & 0x20) {
		wprintf(L"Guiding south...\n");
		sprintf(str, ":Mgs%04d#", msec);
		sersend(hSerial, str);
	}
	if(direction & 0x10) {
		wprintf(L"Guiding east...\n");
		sprintf(str, ":Mge%04d#", msec);
		sersend(hSerial, str);
	}
	Sleep(msec);
}

/******************************************************************
 *		ProgramCamera (CMOSDLL.12)
 *
 *
 */
__stdcall void ProgramCamera(int a, int b, int width, int height, int e)
{
	FIXME(L"ProgramCamera %dx%d %08x %08x %08x\n", width, height, a, b, e);
}

/******************************************************************
 *		SETBUFFERMODE (CMOSDLL.15)
 *
 *
 */
void __stdcall SETBUFFERMODE(void *ptr)
{
	FIXME(L"SETBUFFERMODE\n");
}

/******************************************************************
 *		SetNoiseReduction (CMOSDLL.20)
 *
 *
 */
__stdcall void SetNoiseReduction(int nr)
{
	FIXME(L"SetNoiseReduction\n");
}

/******************************************************************
 *		ThreadedExposure (CMOSDLL.22)
 *
 *
 */
__stdcall void ThreadedExposure(int msec, int b)
{
	char str[4096];
	FIXME(L"ThreadedExposure %dmsec %08x\n", msec, b);
	if(sock > 0) {
		sprintf(str, "set track.exp.exptime %6.4f", msec / 1000.0);
		socksend(sock, str);
		recv(sock, str, sizeof(str), 0);

		socksend(sock, "exp track");
		recv(sock, str, sizeof(str), 0);
	}
}

/******************************************************************
 *		isExposing (CMOSDLL.33)
 *
 *
 */
int __stdcall isExposing()
{
	char buf[4096], *ptr;
	int status;
	if(sock > 0) {
		socksend(sock, "get track.stat");
		recv(sock, buf, 4096, 0);
		if((ptr = strstr(buf, "(status "))) {
			status = strtol(ptr + 8, NULL, 0);
			wprintf(L"Status = %d\n", status);
			if(status == 2) {
				return 1;
			}
		}
	}
	return 0;
}

/******************************************************************
 *		openUSB (CMOSDLL.34)
 *
 *
 */

int __stdcall openUSB(void *a)
{
	WSADATA wsaData;
	int nCode;
	FIXME(L"openUSB\n");
	if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
		wprintf(L"WSAStartup() returned error code %d\n", nCode);
	}
	sock = cx_connect("localhost", 5555);

	return (int) 1;
}

/******************************************************************
 *		closeUSB (CMOSDLL.26)
 *
 *
 */
int __stdcall closeUSB(void)
{
	FIXME(L"closeUSB\n");
	if(hSerial != 0 && hSerial != INVALID_HANDLE_VALUE) {
		CloseHandle(hSerial);
	}
	if(sock > 0) {
		closesocket(sock);
	}
	WSACleanup();
	return (int) 0;
}

